from datetime import datetime
import time
import logging

import threading


class BaseLoggerHandler(logging.Handler):

    def __init__(self, domain, serverProxy):
        self.domain = domain
        self.proxy = serverProxy
        logging.Handler.__init__(self)

    def emit(self, record):
        msg = record.msg
        if isinstance(msg, (list, tuple)):
            key = msg[0]
            record.msg = msg[1]
        else:
            key = ''
        try:
            self._log(domain=self.domain,
                      context=record.name,
                      msg_key=key,
                      msg_value=self.format(record),
                      timestamp=record.created,
                      eventtype=record.levelname)
        except (KeyboardInterrupt, SystemExit):
            raise
        except:
            self.handleError(record)


class LoggerHandler(BaseLoggerHandler):

    def _log(self, domain, context, msg_key, msg_value, timestamp, eventtype):
        self.proxy.log(domain,
                       context,
                       msg_key,
                       msg_value,
                       timestamp,
                       eventtype)


pushThread = None
pushTrigger = None
pushLock = threading.RLock()
pushQueue = []

class ThreadedLoggerHandler(BaseLoggerHandler):

    def __init__(self, domain, serverProxy):
        BaseLoggerHandler.__init__(self, domain, serverProxy)
        pushLock.acquire()
        try:
            global pushThread, pushTrigger
            if pushThread is None:
                pushTrigger = threading.Event()
                pushThread = LoggerPushThread()
                pushThread.setDaemon(True)
                pushThread.logger = self
                pushThread.start()
        finally:
            pushLock.release()

    def _log(self, **kwargs):
        global pushQueue
        pushLock.acquire()
        try:
            pushQueue.append(kwargs)
        finally:
            pushLock.release()
            pushTrigger.set()

    def _batchLog(self, entries):
        self.proxy.batchLog(entries)


# here we define a non propagatin logger used in the logger thread to log
# exceptions to stdout

threadLogger = logging.getLogger(__name__ + '.LoggerPushThread')
threadLogger.propagate = 0
threadLogger.addHandler(logging.StreamHandler())
threadLogger.setLevel(logging.INFO)

class LoggerPushThread(threading.Thread):

    def run(self):
        global pushQueue
        while True:
            pushTrigger.wait()
            pushLock.acquire()
            try:
                if len(pushQueue) > 100:
                    threadLogger.info('pushQueue overflow : removing all but 100')
                    del pushQueue[:-100]
                entries = list(pushQueue[:10])
            finally:
                pushLock.release()
            try:
                self.logger._batchLog(entries)
                pushLock.acquire()
                try:
                    pushQueue = pushQueue[len(entries):]
                    if not pushQueue:
                        pushTrigger.clear()
                finally:
                    pushLock.release()
            except:
                threadLogger.exception('Remote Logging error : Retry in 10 seconds')
                time.sleep(10)

